//======== Programming Assignment 2: Deadlock Scenario ======================
//===========================================================================
//===========================================================================
//===========================================================================
//===========================================================================
#include <stdio.h>
#include <iostream.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sync.h>
#include <sys/siginfo.h>
#include <sys/neutrino.h>
#include <sys/netmgr.h>
#include <sys/syspage.h> 
//=============================================================================

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

#define PCnt 10 /* Maximum number of threads*/
#define eps .005 
float priority[PCnt]={0}; // priority of threads 
int active_p=0;			  // detemine the active threas that should be run	


void ThreadManager();



//=========================== Use the timer class that you developed for programming assignment 1   ===========================
class Timer
{
public:
	int channel_id;
	
	Timer(){
		if ((channel_id = ChannelCreate (0)) == -1) {
			perror (NULL);
			return;
		}
	}
	void wait()
	{
		MsgReceive (channel_id, NULL, NULL, NULL);
	
	}
	
	void setup(unsigned int seconds)
	{
		timer_t    timerid;    
		sigevent   event;     
	    itimerspec timer;  
	    int        coid;     
	     
		// connection back to the previous channel created
	    coid = ConnectAttach (0, 0, channel_id, 0, 0);
	     
	    if (coid == -1) {
	    	printf("Error coid");
	    	perror (NULL);
	    	return;
	    	//return 0;
	    }
		     
		// macro to create a pulse event
		SIGEV_PULSE_INIT (&event, coid, SIGEV_PULSE_PRIO_INHERIT, 1, 0);
		  
		//creating the timer object
		if (timer_create (CLOCK_REALTIME, &event, &timerid) == -1) {
		    perror(NULL);
		    printf("error"); 
		    return;
		    //return(0);
		}
	     
		// configuring the timer with the arguments
		timer.it_value.tv_sec = seconds;
		timer.it_value.tv_nsec = 0;
		timer.it_interval.tv_sec = seconds;
		timer.it_interval.tv_nsec = 0;

		// finally, we start it
		timer_settime (timerid, 0, &timer, NULL);
	}
};

#define PRIORITY_INHERITANCE
#ifdef PRIORITY_CEILING 
//============================================  develope the mutex class here ====================================================
class CS  // The developed Mutex class 
{
	private:
		
		int heldBy;
		
		float initialPriority;
	public:
		volatile int s;								// mutex status; 0 is free 1 is taken
		CS ();
		~CS();
		float PC;
		void lock(int p, CS sp[]);
		void unlock(int p);
};

CS::CS() {
	s=0;    
}

CS::~CS(){
}


void CS::lock(int p, CS sp[]){
	//cout<< "LOCKed by P"<<p;	 // display a message
		for (int i = 0 ;i < PCnt ; i++)
		{
			if( sp[i].s != 0 && sp[i].heldBy != p )
			{
				cout << "P" << sp[i].heldBy << " is holding CS " << i << endl;
				cout << "P" << sp[i].heldBy << " is of lower priorty; elevating its priority" << endl; 
				
				sp[i].initialPriority = priority[ sp[i].heldBy ];
				cout << "Keeping P" << sp[i].heldBy << "'s initial priority of " << sp[i].initialPriority <<endl;
				priority[sp[i].heldBy] = sp[i].PC;
				cout << "Elevating P" << sp[i].heldBy << " to " << priority[sp[i].heldBy]<< endl;
				pthread_mutex_unlock(&mutex);
				while(sp[i].s==1){
					
				
				}
				
			}
		}

		s = 1; //"""LOCK""" the mutex
		this->heldBy = p;
		cout << "Lock unused. Process P" << p << " taking lock." << endl;
		

	
}

void CS::unlock(int p){
	cout<< "UNLOCKed by P"<<p;	 // display a message
	this->s = 0;
	if(this->initialPriority != 0){
		priority[p] = this->initialPriority;
		this->initialPriority = 0;
	}
	
	
	
	cout << "P2 priority " << priority[2] << endl;
}
#endif

#ifdef PRIORITY_INHERITANCE
//============================================  develope the mutex class here ====================================================
class CS  // The developed Mutex class 
{
	private:
		
		float initialPriority;
		int heldBy;
		float PC;
	public:
		int s;								// mutex status; 0 is free 1 is taken
		CS ();
		~CS();
		void lock(int p, CS sp[]);
		void unlock(int p);
		
};

CS::CS() {
	s=0;    
	PC = 0.8;
}

CS::~CS(){
}


void CS::lock(int p, CS sp[]){
	if(s == 0){
		cout << "Lock unused. Process P" << p << " taking lock." << endl;
		s = 1; //"""LOCK""" the mutex
		this->heldBy = p;
	}
	else{
		if( priority[p] > priority[this->heldBy]){ //elevate the process currently holding the mutex
			cout << "P" << this->heldBy << " is of lower priorty; elevating its priority" << endl; 
			
			this->initialPriority = priority[this->heldBy];
			cout << "Keeping P" << this->heldBy << "'s initial priority of " << this->initialPriority <<endl;
			priority[this->heldBy] = this->PC;
			cout << "Elevating P" << this->heldBy << " to " << priority[this->heldBy]<< endl;
			pthread_mutex_unlock(&mutex);
			while(s==1);
			//cout << "P" << threadIndex << " locked the mutex" << endl;
		}

		cout << "Lock used by process P" << p << ", increasing its priority." << endl;
	}
}

void CS::unlock(int p){
	cout<<"UNLOCKed by P"<<p;	 // display a message
	if(priority[p] == this->PC){
		cout << "P" << p << "priority is elevated at " << priority[p] << ". Setting it down to " << this->initialPriority <<endl;
		priority[p] = this->initialPriority;
	}
	else
	{
		cout << "P" << p << " never got elevated" << endl;
	
	}
	s = 0;
	
}


#endif

//=============================================================================

//=============================================================================


CS s[PCnt]; // mutexes are instantiated here 

//=============================================================================
void * P1(void* arg)
{
	int cnt=1,j;
	//Message msg;
	//cout << ".........P1 thread starts.........";
	while(1){
		pthread_mutex_lock(&mutex);
		do{
			pthread_cond_wait(&cond,&mutex); // check for a message from ThreadManager
		}while(active_p!=1);				 // check the active thread	

		//if(active_p==1)     				 // check the active thread	
		{
		//+++++++++++++++++++++++++++++++++++++++++++++
			cout<<"P1->";//<<cnt<<endl;
			if(cnt==1){
				cout << ".....CS1 ..";
				s[1].lock(1,s);
			}
			else if(cnt==2){
				cout << ".....CS2 ..";
				s[2].lock(1,s);
				//
				
				//
			}
			else if(cnt==3){
				cout << ".....CS2 ..";
				s[2].unlock(1);
				//
				
				
				//
			} 
			else if(cnt==4){
				cout << ".....CS1 ..";
				s[1].unlock(1);
				
				
			}
			else if (cnt == 6){
				//Message msg;
				cout << ".........P1 thread ends.........";
				priority[1]=0; // to remove process 1 from the queue of ThreadManager
				pthread_mutex_unlock(&mutex);
				break;
			}			
			cnt++;
		}
		//---------------------------------------------
		//pthread_cond_wait(&cond,&mutex); // check for a message from ThreadManager
		pthread_mutex_unlock(&mutex);
	}

}
//=============================================================================
void * P2(void* arg)
{

	int cnt=1,j;
	//Message msg;
	//cout << ".........P2 thread starts.........";

	while(1){
		pthread_mutex_lock(&mutex);
		do{
			pthread_cond_wait(&cond,&mutex); // check for a message from ThreadManager
		}while(active_p!=2);				 // check the active thread	

		//if(active_p==2)     				 // check the active thread	
		{
		//+++++++++++++++++++++++++++++++++++++++++++++
			cout<<"P2->";//<<cnt<<endl;
			if(cnt==1){
				cout << ".....CS2 ..";
				s[2].lock(2,s);
			}
			else if(cnt==4){
				cout << ".....CS1 ..";
				s[1].lock(2,s);
			}
			else if(cnt==5){
				cout << ".....CS1 ..";
					s[1].unlock(2);
					cout << s[1].s << endl;
			} 
			else if(cnt==6){
				cout << ".....CS2 ..";
				s[2].unlock(2);
				cout << s[2].s << endl;
			}
			else if (cnt == 7){
				//Message msg;
				cout << ".........P2 thread ends.........";
				priority[2]=0; // to remove process 2 from the queue of ThreadManager
				pthread_mutex_unlock(&mutex);
				break;
			}			
			cnt++;
		}
		//---------------------------------------------
		//pthread_cond_wait(&cond,&mutex); // check for a message from ThreadManager
		pthread_mutex_unlock(&mutex);
	}
}


void ThreadManager(){ // determines that which thread should be run
	float p;	
	int i;

	pthread_mutex_lock(&mutex);
	//cout << "P1 priority " << priority[1] << endl;
	//cout << "P2 priority " << priority[2] << endl;
	
		p=-1;
		for(i=1;i<PCnt;i++){ // find the thread with the most priority and set it as active thread
			if(priority[i]>p){
				active_p=i;
				p=priority[i];
			}
		}
		pthread_mutex_unlock(&mutex);
		pthread_cond_broadcast(&cond); // send the signal to the threads
}

//=============================================================================
//                                 M     A     I   N
//=============================================================================
int main(void)
{
	pthread_t P1_ID,P2_ID;       //p1, p2, threads
	int cnt=0,i;
	
	#ifdef PRIORITY_CEILING
	//set the priority ceiling of the mutexex

	s[1].PC=0.8; 
	s[2].PC=0.8;
	
	#endif


	//creating up a periodic  timer to generate pulses every 1 sec.
	Timer t;
	t.setup(1);
	while(1)
	{
		//cout << "priority of P1 " << priority[1] << endl;
		//cout << "priority of P2 " << priority[2] << endl;
		
		//--------------------------------------------		
		pthread_mutex_lock(&mutex);
		 // release P2 at t=0
		if(cnt==0){ 
			priority[2]=.5;
			pthread_create(&P2_ID , NULL, P2, NULL);
		}
		 // release P1 at t=2
		else if(cnt==2){
			priority[1]=.7;
			pthread_create(&P1_ID , NULL, P1, NULL);
		}
		 // terminate the program at t=30
		else if (cnt == 30){
			break;
		}			
		pthread_mutex_unlock(&mutex);
		//---------------------------------------------
		// waite for the timer pulse
		t.wait();
		//+++++++++++++++++++++++++++++++++++++++++++++
		cnt++;
		// message
		cout<<endl<<"tick="<<cnt<</*", active_p="<<active_p<<*/"->";//<<endl;

		ThreadManager(); // to find out and run the active thread

    	//*********************************************
	}
}
//=============================================================================

